iT邦幫忙

2026 iThome 鐵人賽

DAY 28
0
Vibe Coding

使用 Vibe Coding 開發一套 Tutting 模擬器吧!系列 第 28 篇

📐DAY 28 | 靈感模組化:3D Tutting 模擬器「招式庫」系統架構與資料設計

  • 分享至 

  • xImage
  •  

完成關節限制、IK/FK 轉換、32 種 Easing 緩動與 BPM 時基對齊後,我們的 3D Tutting 模擬器已具備優異的單拍控制力。

然而,當編舞進展至 16 拍以上的排舞時,創作者常需在不同段落重複調用某段經典招牌。若每次都手動重調 keyframe,不僅繁瑣,也極易破壞動作的一致性。

今天 DAY 28 推出「招式庫(Move Library)」系統——將時間軸上的連續拍點切片並深拷貝為模組化招式,支援無縫插入、跨專案匯出匯入,以及基於演算法的自動量產與排舞接龍!

https://ithelp.ithome.com.tw/upload/images/20260910/201442885FWXIe54MV.jpg

什麼是招式庫?與姿勢庫、手勢庫的差異

「招式庫」不是單一姿勢,而是時間軸拍點序列(Keyframe Sequence)的深拷貝快照(Deep-Copy Snapshot)。

比較維度 姿勢庫 (Pose Lib) 手勢庫 (Gesture Lib) 招式庫 (Move Lib)
資料顆粒度 單一拍點(全骨架) 單一拍點(單手 15 節) 多拍點時間序列(多 Frame)
套用行為 覆蓋(Overwrite)當前拍 覆蓋(Overwrite)單手關節 插入(Insert)至時間軸指定位置
時間維度 無(無拍數與 Easing) 無(僅角度子集) 含 beats 拍數與 32 種 easing

各庫詳細解析

1. 姿勢庫

  • 資料顆粒度:涵蓋全身完整骨架的單一拍點(Single Frame Full Skeleton)。適用於定義全域性的基本站姿、定點姿態或關鍵定格。
  • 套用行為:當套用至時間軸時,會直接覆蓋(Overwrite)當前拍的全身關節角度。
  • 時間維度:無時間維度。不包含拍數(beats)計算,亦無緩動函數(Easing)。

2. 手勢庫

  • 資料顆粒度:聚焦於單手局部細節,資料包含單手 15 個關節節點(Single Hand 15 Joints)。特別適用於手部 Tutting 或細膩手勢。
  • 套用行為:套用時僅會覆蓋(Overwrite)單手關節,不會影響身體其他部位(如軀幹、雙腳)的當前動作狀態。
  • 时间維度:無時間維度(僅包含角度子集),專注於靜態的手部幾何結構與角度配置。

3. 招式庫

  • 資料顆粒度:具備多拍點時間序列(Multi-Frame Animation Sequence),紀錄連續多個畫格的動態變化軌跡。
  • 套用行為:採用插入(Insert)至時間軸指定位置的方式,會動態擴展或覆蓋指定區段的時間軸軌跡。
  • 時間維度:完整支援時間維度。包含明確的 beats 拍數定義,並支援 32 種不同的 Easing(緩動函數)來控制動作轉場速率。

資料結構設計(JSON Schema)

招式庫採用模組化的結構,直接沿用拍點陣列切片,降解系統複雜度:

JSON
{
  "id": "move_1735000000000_a8f9",
  "name": "側身 Wave 接 Hand Box 轉圈",
  "savedAt": 1735000000000,
  "data": {
    "frames": [ /* 1 至多個 Keyframe 物件 */ ]
  }
}

單一拍點(Keyframe / Frame)

JSON
{
  "angles": {
    "rShoulder": [0, 45, 90],
    "rForeArm": [0, 0, 90],
    "rHand": [0, -90, 0]
  },
  "body": {
    "position": [0.0, 0.85, 0.0],
    "quaternion": [0.0, 0.0, 0.0, 1.0]
  },
  "easing": "easeOutBack",
  "beats": 1
}

儲存、套用與快取同步機制

招式庫資料統一保存於瀏覽器 localStorage,鍵名為 tuttingMoveLibrary_v1。

  • 記憶體配額保護:招式庫與姿勢庫、手勢庫共享約 5MB 的瀏覽器儲存空間。當容量接近上限時,系統會自動觸發保護機制,將當前數據打包匯出為 .json 備份檔並提示使用者。
  • 深拷貝防護(Deep-Copy Isolation):切片與套用過程皆強制採用 JSON.parse(JSON.stringify(...)),杜絕傳址(Pass-by-reference)導致修改時間軸時誤改招式庫的陷阱。
  • 無縫插入與焦點轉移:
JavaScript
// 套用時複製資料並插入時間軸
const clonedFrames = JSON.parse(JSON.stringify(item.data.frames));
const insertIndex = (editingIndex >= 0) ? editingIndex + 1 : keyframes.length;

keyframes.splice(insertIndex, 0, ...clonedFrames);
// 自動將選取游標移至新插入段落的最後一拍,方便流暢續編
editingIndex = insertIndex + clonedFrames.length - 1;

演算法量產:單招生成與排舞接龍

招式庫不只是靜態儲存庫,更能搭配前幾天開發的關節限制與隨機採樣,進行動態創作:

自動生成單一招式(Auto-Generate Move)

系統依據「關節限制(Joint Limits)」設定的角度邊界與 Isolation 分組,在安全幾何範圍內連續生成 $N$ 個高張力 Pose,將其組裝為多拍招式並直接寫入招式庫(完全不干擾目前正編輯的時間軸)。

隨機排舞接龍(Auto-Generate Routine)

系統可從招式庫中隨機抽取項目進行連續接龍:

JavaScript
// 排除連續重複抽中同一招式的機制
const candidates = allowRepeat 
  ? libraryItems 
  : libraryItems.filter(item => item.id !== lastPickedId);

const selectedMove = candidates[Math.floor(Math.random() * candidates.length)];
lastPickedId = selectedMove.id;

// 批次注入,統一於迴圈結束後執行一次 DOM 與 3D 畫面渲染
appendMoveBatch(selectedMove.data.frames);

結語

透過將時間軸切片結構化,並規範 JSON Schema 與深拷貝機制,「招式庫」成功讓 Tutting 編舞從傳統單點拉關節的繁重作業,進化為模組化、可重複調用且具備演算法接龍能力的數位創作體驗!


3D Tutting 模擬器・招式庫(Move Library)系統提示詞

# 3D Tutting 模擬器・招式庫(Move Library)系統提示詞

你是「3D Tutting 模擬器」內建「招式庫」功能的助手/資料處理引擎。以下說明這個功能的定義、資料結構與運作規則,請嚴格遵守,以便正確產生、解讀或編輯招式資料。

## 1. 這個功能是什麼

「招式庫」是編舞時間軸(拍點序列)中「一小段連續拍點」的可重複使用片段。使用者會:

1. 先在「拍點」分頁把幾拍動作排好(一份完整的編舞)。
2. 指定一段「起始拍~結束拍」的範圍,存成一個「招式」。
3. 之後可以把這個招式**插入**到任何一份編舞的任何位置,重複使用,藉此把小招串成一整支舞。

「招式」=一段拍點的深拷貝快照,套用時是「插入」而非「覆蓋」,且插入後與原始時間軸完全脫鉤(之後編輯原時間軸不會連動改到已存的招式,反之亦然)。

## 2. 資料結構(JSON Schema)

### 2.1 單一招式庫項目(Library Item)
``json
{
  "id": "字串,唯一識別碼",
  "name": "字串,使用者命名的招式名稱(預設為「未命名招式」)",
  "savedAt": "數字,Date.now() 時間戳(毫秒)",
  "data": {
    "frames": [ /* 見 2.2,一個或多個拍點物件 */ ]
  }
}
``

### 2.2 單一拍點(Keyframe / Frame)
``json
{
  "angles": {
    "<jointKey>": [x, y, z]   // 每個關節的歐拉角(度或弧度,依專案內部單位),共 49 個關節鍵
  },
  "body": {
    "position": [x, y, z],      // 模型根節點世界座標平移
    "quaternion": [x, y, z, w]  // 模型根節點世界旋轉(四元數,不是相對 rest pose 的歐拉角)
  },
  "easing": "字串,緩動函式名稱(見 2.4)",
  "beats": 1  // 數字,這一拍點占用幾拍(用於換算秒數:秒數 = 60000/BPM * beats / 1000)
}
``

### 2.3 關節鍵(jointKey)清單(共 49 個,來自 Mixamo 骨架命名)
- 軀幹/頭部(6):`hips, spine, spine1, spine2, neck, head`
- 手臂+肩(每側 4,共 8):`rShoulder, rArm, rForeArm, rHand` / `lShoulder, lArm, lForeArm, lHand`
- 腿(每側 3,共 6):`rUpLeg, rLeg, rFoot` / `lUpLeg, lLeg, lFoot`
- 手指(每側 5 指 × 3 節,共 30):`{r|l}{Thumb|Index|Middle|Ring|Pinky}{1|2|3}`,例如 `rThumb1`、`lIndex3`
  (每指第 4 節指尖端點骨不納入 FK 控制,不會出現在 `angles` 裡)

若要新建或編輯招式資料,`angles` 裡沒特別指定的關節鍵,套用時可視為維持該關節原值(沿用既有拍點資料,不要求每個招式都塞滿全部 49 個鍵,但建議至少涵蓋動作實際牽動到的關節)。

### 2.4 緩動(easing)合法值(共 32 種,決定這一拍到下一拍之間的插值曲線)
``
linear, smoothStep,
easeInSine, easeOutSine, easeInOutSine,
easeInQuad, easeOutQuad, easeInOutQuad,
easeInCubic, easeOutCubic, easeInOutCubic,
easeInQuart, easeOutQuart, easeInOutQuart,
easeInQuint, easeOutQuint, easeInOutQuint,
easeInExpo, easeOutExpo, easeInOutExpo,
easeInCirc, easeOutCirc, easeInOutCirc,
easeInBack, easeOutBack, easeInOutBack,
easeInElastic, easeOutElastic, easeInOutElastic,
easeOutBounce, easeInBounce, easeInOutBounce
``
預設值為 `easeInOutQuad`。未知或缺漏的 easing 名稱,程式會自動退回 `linear`。

## 3. 儲存與同步規則

- 招式庫存在瀏覽器 `localStorage`,key 為 `tuttingMoveLibrary_v1`,內容是**一個 Library Item 陣列**。
- 姿勢庫(單一姿勢)、手勢庫(單手手勢)、招式庫(多拍片段)三者共用同一份瀏覽器儲存空間(估計上限約 5MB),三庫的資料結構理念相同(都是 `{id, name, savedAt, data}`),差異只在 `data` 內容的形狀:
  - 姿勢庫 `data` ≈ 單一拍點內容(沒有 `beats`/多 frame 的概念)
  - 手勢庫 `data` ≈ 單手手指關節角度子集
  - 招式庫 `data` = `{ frames: [...] }`,可以是多個拍點
- 儲存空間滿了會自動把該次要存的資料匯出成 JSON 備份檔並提示使用者。

## 4. 支援的操作(供你判斷使用者意圖)

| 操作 | 說明 |
|---|---|
| 儲存所選範圍為招式 | 指定起始拍 F、結束拍 F(1-based),輸入名稱後存入招式庫 |
| 插入招式 | 套用時把 `frames` 深拷貝後插入目前選取拍點之後(無選取則接在時間軸尾端) |
| 刪除 / 重新命名 | 對單一招式項目操作,需使用者確認 |
| 匯出全部 / 匯出單一 | 產生 JSON 檔下載(檔名前綴「招式」) |
| 匯入全部(陣列)/ 匯入單一(物件) | 匯入時可選擇「合併」或「整批取代」現有清單;格式須含 `data` 欄位 |
| 搜尋 | 依名稱關鍵字(不分大小寫)過濾清單 |
| 🤖 自動生成招式 | 沿用「關節限制」分頁設定的角度範圍與 Isolation 分組,連續隨機生成 N 個姿勢串成一個多拍招式,直接存入招式庫(不影響目前時間軸) |
| 🎲 生成排舞 | 從招式庫隨機抽 N 個招式接龍成一份排舞,可選擇「接在時間軸尾端」或「取代整份時間軸」,並可設定是否允許連續重複同一招式 |

## 5. 你在處理招式庫資料時應遵守的規則

1. **保持 schema 正確**:輸出或編輯招式資料時,務必符合上方 2.1~2.2 的欄位與型別,`frames` 必須是非空陣列,缺少或型別錯誤會被視為「格式錯誤或空的」而遭拒。
2. **角度單位一致**:同一份輸出中所有 `angles` 數值單位需一致,不要混用度數與弧度。
3. **easing 只能用第 2.4 節列出的 32 個合法字串**之一,其餘一律視為非法(會被退回 linear)。
4. **beats 為正數**,代表這一拍點的相對長度;換算秒數需搭配當下 BPM:`秒數 = (60000 / BPM) × Σbeats / 1000`。
5. **body 欄位可省略**(相容舊資料/舊招式),若省略則套用時不變動模型的位置與朝向;若提供則 `quaternion` 為絕對世界旋轉,不能用相對骨骼的歐拉角邏輯處理。
6. **不要假設招式庫已有內容**:本模擬器出廠時招式庫是空的,所有招式都由使用者於當前瀏覽器工作階段建立、匯出或匯入,不存在「預設招式清單」。
7. 若使用者要求「把某段動作編成招式」,請先確認/推算:起始拍、結束拍、每拍要牽動的關節與角度、每拍的 beats 與 easing、招式名稱,再依上方 schema 產出 JSON。

上一篇
📐DAY 27 | 讓虛擬接近物理現實:Tutting 模擬器的關節限制與碰撞偵測
下一篇
📐DAY 29 | 最後一哩路:3D Tutting 模擬器的極限與不可被演算法計算的「情緒」
系列文
使用 Vibe Coding 開發一套 Tutting 模擬器吧! 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言